Skip to content

Editor: Stop :has() selectors recalculating the whole document on every block selection - #81471

Merged
Mamaduka merged 1 commit into
trunkfrom
perf/block-selection-has-selectors
Aug 12, 2026
Merged

Editor: Stop :has() selectors recalculating the whole document on every block selection#81471
Mamaduka merged 1 commit into
trunkfrom
perf/block-selection-has-selectors

Conversation

@Mamaduka

Copy link
Copy Markdown
Member

What?

Related #81457.

Rewrites four :has() rules in the editor and interface styles so that each :has() compound is the subject of its own rule.

Why?

Clicking a paragraph in a 1000-paragraph post ran about 8 full document-style recalcs in the post.php frame. The cause is body:has(.editor-editor-interface.is-distraction-free) #wpadminbar { display: none }. Blink treats a :has() whose subject sits below the anchor differently from one where the anchor is the subject: it cannot tell which descendants are affected, so it flags the anchor's entire subtree and recalculates it on every DOM change during the React commit. The same pattern appeared in the revisions timeline and the collab sidebar rules.

Splitting the distraction-free rule in two confirmed where the cost was. The inherited custom property half is free, and the #wpadminbar half accounts for all of it.

How?

The distraction-free state now reaches the admin bar through a second inherited custom property, so body stays the subject of its own :has(). The revisions timeline rules are unnested, since their subjects exist only in revisions mode and do not require the extra scoping. The collab sidebar header is targeted by its own class instead of a :has() on the skeleton sidebar.

Results

Measured across two real builds with the arms alternating within one session, since sequential A/B blocks are not reliable at this size.

Metric Trunk This PR
Selecting blocks perf metric (median of 40 clicks) 32.86 ms 27.32 ms
First click after load (median) 55.5 ms 43.6 ms
Style recalc, first click 20.3 ms 6.3 ms
Elements recalculated, first click 6178 476
Largest single recalc pass 850 elements 142 elements
BODY subtree invalidations per click 8 0

The document has 792 elements, so the whole document recalcs are gone.

The Selecting blocks metric discards the first click, so it reports the steady state improvement of about 17 percent rather than the larger first click one. Head won all four rounds with no overlap between the per-round ranges.

Testing Instructions

Some of these have e2e test coverage, so the smoke test affected features:

  • Enabled distraction-free mode. The adminbar should be hidden.
  • Open editor revisions. The sidebar should be rendered as before, no double scrollbars.
  • The floating notes sidebar shouldn't render a header and shadow.

Testing Instructions for Keyboard

Same.

Use of AI Tools

Assisted by Claude

@Mamaduka Mamaduka self-assigned this Aug 12, 2026
@Mamaduka Mamaduka added the [Type] Performance Related to performance efforts label Aug 12, 2026
@github-actions github-actions Bot added [Package] Editor /packages/editor [Package] Edit Post /packages/edit-post [Package] Edit Site /packages/edit-site labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown

Size Change: +80 B (0%)

Total Size: 7.71 MB

📦 View Changed
Filename Size Change
build/styles/edit-post/style-rtl.css 3.34 kB +17 B (+0.51%)
build/styles/edit-post/style-rtl.min.css 1.99 kB +13 B (+0.66%)
build/styles/edit-post/style.css 3.34 kB +17 B (+0.51%)
build/styles/edit-post/style.min.css 1.99 kB +13 B (+0.66%)
build/styles/edit-site/style-rtl.css 21.6 kB +16 B (+0.07%)
build/styles/edit-site/style-rtl.min.css 17.7 kB +13 B (+0.07%)
build/styles/edit-site/style.css 21.6 kB +17 B (+0.08%)
build/styles/edit-site/style.min.css 17.7 kB +13 B (+0.07%)
build/styles/editor/style-rtl.css 31.7 kB -12 B (-0.04%)
build/styles/editor/style-rtl.min.css 27 kB -7 B (-0.03%)
build/styles/editor/style.css 31.8 kB -13 B (-0.04%)
build/styles/editor/style.min.css 27 kB -7 B (-0.03%)

compressed-size-action

@Mamaduka

Copy link
Copy Markdown
Member Author

The Selecting blocks metric discards the first click.

I think we should remove this throwaway case, as it can mask an actual regression. For example, the editable root change (removed in #81184) affected the first selection significantly, but switching between blocks was cheaper. So, as a user opening a large post, the first selection was junky, but this was missed by our specs.

The downside is that our metrics will probably jump, but they'll show numbers close to the actual median.

cc @ellatrix, @youknowriad, @tyxla

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@Mamaduka

Copy link
Copy Markdown
Member Author

Here are the metrics from the last two CI runs.

Screenshots

CleanShot 2026-08-12 at 13 33 45 CleanShot 2026-08-12 at 14 33 59

@youknowriad

Copy link
Copy Markdown
Contributor

Really cool. I didn't know a CSS change could have such a huge impact on our metrics. Good job.

On the ignoring of the first selection, I wonder if it means we should have two metrics, first selection, consecutive ones. I guess my main concern is whether we're computing an average that doesn't make sense.

@Mamaduka

Copy link
Copy Markdown
Member Author

I need to check the Git history and confirm if adding a throwaway step was intentional or just something copied and pasted from an existing spec.

Maybe we should measure the average of the first block selection (clear selection between iterations). This is usually when regressions are noticeable, since controls need to be mounted, etc.

Consecutive ones or switching between blocks is also interesting, but here we have more variables. Switching between the same block types can be faster than switching from Paragraph to Navigation or any other block with complex controls.

@youknowriad

Copy link
Copy Markdown
Contributor

I think I added it intentionally but that was a long time ago haha

@youknowriad

Copy link
Copy Markdown
Contributor

Maybe we should measure the average of the first block selection (clear selection between iterations). This is usually when regressions are noticeable, since controls need to be mounted, etc.

Yes I like that.

@Mamaduka

Copy link
Copy Markdown
Member Author

I'll follow up on that separately, also curious if @ellatrix has any suggestions.

Meanwhile, should we merge this?

// in the editor recalculates the whole document.
&:has(.editor-editor-interface.is-distraction-free) {
--wp-admin--admin-bar--height: 0px;
--wp-editor-admin-bar-display: none;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know some of this is pre-existent, But I'd prefer that we avoid CSS variables as much as possible as they become kind of APIs. Might not be always possible though.

@youknowriad

Copy link
Copy Markdown
Contributor

I'm seeing an issue but it might not be specific to this PR. I have "show template" enabled, I go into "distraction free" and when I click "styles" in the more menu, nothing happens, the styles sidebar doesn't show up and there's also a double scrollbar I think.

@youknowriad youknowriad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me (need to confirm whether that previous bug is specific to this branch or not)

@Mamaduka

Copy link
Copy Markdown
Member Author

I can reproduce the Styles more menu item issue on trunk as well. It's an existing bug.

@Mamaduka
Mamaduka merged commit 7455cde into trunk Aug 12, 2026
64 of 67 checks passed
@Mamaduka
Mamaduka deleted the perf/block-selection-has-selectors branch August 12, 2026 15:30
@github-actions github-actions Bot added this to the Gutenberg 23.9 milestone Aug 12, 2026
shail-mehta pushed a commit that referenced this pull request Aug 12, 2026
…very block selection (#81471)

Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
@jeryj

jeryj commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Found this from checking in on the code vitals. I skipped over this commit initially because I didn't think a :has selector would have such a big performance impact 😮 Great find @Mamaduka!

@Mamaduka

Copy link
Copy Markdown
Member Author

Was reading this X thread recently, which is also an interesting case.

Claude also makes writing perf probes a lot better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Edit Post /packages/edit-post [Package] Edit Site /packages/edit-site [Package] Editor /packages/editor [Type] Performance Related to performance efforts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants